/**
 * \file Namespace.cc
 * \brief Namespace information management facilities
 * \author Corey Tabaka
 */

/*
   Copyright 2006 Corey Tabaka

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include <ktypes.h>
#include <Namespace.h>
#include <platform/Interrupt.h>
#include <Sync.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <syscall.h>

/**
 * Variable list head
 */
static Var *head;

/**
 * Number of variables in the list
 */
static uint32 numVars;

/**
 * List lock
 */
static InterruptLock lock;

void Namespace::initialize(void) {
	Interrupt::registerHandler(INT_SYSCALL, syscallHandler);
}

#define _CLASS (syscallParam(regs, 0))
#define _FUNC (syscallParam(regs, 1))
#define _ARG(n) (syscallParam(regs, (n)+2))

bool Namespace::syscallHandler(regs_t *regs) {
	bool handled = false;
	
	if (_CLASS == SYSCALL_VAR) {
		switch (_FUNC) {
			case SYSCALL_VAR_IS_KEY:
				regs->eax = Namespace::isKey((char *) _ARG(0));
				break;
				
			case SYSCALL_VAR_GET_STRING: {
				char *dest = (char *) _ARG(1);
				int len = _ARG(2);
				
				const char *value = Namespace::getString((char *) _ARG(0));
				strncpy(dest, value, len);
				
				int slen = strlen(value);
				
				regs->eax = len < slen ? len : slen;
			}
			break;
				
			case SYSCALL_VAR_GET_INT: {
				long long value = Namespace::getInt((char *) _ARG(0));
				regs->eax = (long) value;
				regs->edx = (long) (value >> 32);
			}
			break;
				
			case SYSCALL_VAR_SET_STRING:
				regs->eax = Namespace::set((char *) _ARG(0), (char *) _ARG(1));
				break;
				
			case SYSCALL_VAR_SET_INT:
				regs->eax = Namespace::set((char *) _ARG(0), _ARG(1));
				break;
				
			case SYSCALL_VAR_CREATE_STRING:
				Namespace::create((char *) _ARG(0), (char *) _ARG(1), VF_USER);
				break;
				
			case SYSCALL_VAR_CREATE_INT:
				Namespace::create((char *) _ARG(0), _ARG(1), VF_USER);
				break;
				
			default:
				printf("Unknown SYSCALL_VAR subfunction %x\n", _FUNC);
			break;
		}
		
		handled = true;
	}
	
	return handled;
}	

bool Namespace::isKey(const char *key) {
	for(Var *v=head; v; v=v->next){
		if(!strcmp(key, v->name)){
			return true;
		}
	}
	return false;
}

void Namespace::create(const char *key, long long value, int flags) {
	if (!set(key, value)) {
		Var *v = new Var(key, value, flags | VF_HEAP);
		registerVar(*v);
	}
}
	
		
void Namespace::create(const char *key, const char *str, int flags) {
	if (!set(key, str)) {
		Var *v = new Var(key, str, flags | VF_HEAP);
		registerVar(*v);
	}
}

const char *Namespace::getString(const char *key) {
	char *ret = "";
	
	lock.lock();
	
	for (Var *v=head; v; v=v->next) {
		if (!strcmp(key, v->name)) {
			ret = v->svalue;
			break;
		}
	}
	
	lock.unlock();
	
	return ret;
}
	
long long Namespace::getInt(const char *key) {
	long long ret = 0;
	
	lock.lock();
	
	for (Var *v=head; v; v=v->next) {
		if (!strcmp(key, v->name)) {
			ret = v->ivalue;
			break;
		}
	}
	
	lock.unlock();
	
	return ret;
}

bool Namespace::set(const char *key, long long value) {
	bool ret = false;
	
	lock.lock();
	
	for (Var *v=head; v; v=v->next) {
		if (!strcmp(key, v->name)) {
			v->set(value);
			ret = true;
			break;
		}
	}
	
	lock.unlock();
	
	return ret;
}

bool Namespace::set(const char *key, const char *value) {
	bool ret = false;
	
	lock.lock();
	
	for (Var *v=head; v; v=v->next) {
		if (!strcmp(key, v->name)) {
			v->set(value);
			ret = true;
			break;
		}
	}
	
	lock.unlock();
	
	return ret;
}

void Namespace::registerVar(Var &var) {
	lock.lock();
	
	if (head) {
		// search for a var of the same name to prevent duplicates
		Var *prev = NULL;
		for (Var *v=head; v; v=v->next) {
			if (!strcmp(var.name, v->name)) {
				// TODO: check for combinations of VF_USER and VF_SYSTEM flags
				// to decide what to do here about removing the existing var
				
				// remove the old var from the list
				if (prev) {
					prev->next = v->next;
				}
				
				// if the var is dynamically allocated, delete it
				if (v->flags & VF_HEAP) {
					delete v;
				}
				
				numVars--;
				break;
			}
			prev = v;
		}
	}
	
	// add the var at the front of the list
	var.next = head;
	head = &var;
	numVars++;
	
	lock.unlock();
}

void Namespace::unregisterVar(Var &var) {
	lock.lock();
	
	if (head == &var) {
		head = var.next;
	} else {
		for (Var *v=head; v; v=v->next) {
			if (v->next == &var) {
				v->next = var.next;
				
				if (var.flags & VF_HEAP) {
					delete &var;
				}
				break;
			}
		}
	}
	
	numVars--;
	
	lock.unlock();
}

void Namespace::dumpVars(const char *prefix) {
	for (Var *v=head; v; v = v->next) {
		printf("%s %s %s %llu\n", (prefix ? prefix : ""), v->name, v->svalue,
			v->ivalue);
	}
}

Var::Var(const char *name, long long ivalue, int flags){
	this->name = new char[strlen(name)+1];
	strcpy(this->name, name);

	this->svalue = NULL;
	set(ivalue);
	this->flags = flags;
}

Var::Var(const char *name, const char *svalue, int flags){
	this->name = new char[strlen(name)+1];
	strcpy(this->name, name);

	this->svalue = NULL;
	set(svalue);
	this->flags = flags;
}

Var::~Var(void) {
	delete name;
	name = NULL;
	
	delete svalue;
	svalue = NULL;
}

void Var::set(const char *svalue) {
	if (this->svalue) {
		delete this->svalue;
	}
	
	this->svalue = new char[strlen(svalue)+1];
	strcpy(this->svalue, svalue);

	this->ivalue = atol((char *) svalue);
}

void Var::set(long long ivalue) {
	if (this->svalue) {
		delete this->svalue;
	}
	
	char str[64];
	sprintf(str, "%lld", ivalue);
	svalue = new char[strlen(str)+1];
	strcpy(svalue, str);

	this->ivalue = ivalue;
}

long long Var::operator= ( const long long ivalue ) {
	set(ivalue);
	return ivalue;
}

void Var::operator= ( const char *svalue ) {
	set(svalue);
}

void Var::setListener(VarListener *listener) {
	this->listener = listener;
}
